package com.uinnova.product.eam.web.bm.peer;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.lang.StringUtils;
import com.binary.core.util.BinaryUtils;
import com.binary.framework.exception.ServiceException;
import com.uinnova.product.eam.model.dto.VisualModelsDto;
import com.uinnova.product.eam.model.vo.ThumbnailVo;
import com.uinnova.product.eam.model.vo.VisualModelsVo;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCiClass;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.project.base.diagram.util.FileFilterUtil;
import com.uino.api.client.cmdb.IVisualModelApiSvc;
import com.uino.api.client.permission.IUserApiSvc;
import com.uino.bean.cmdb.base.ESVisualModel;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.business.UserInfo;
import com.uino.dao.cmdb.ESVisualModelSvc;
import com.uino.dao.util.ESUtil;
import com.uino.service.cmdb.microservice.ICIClassSvc;
import com.uino.service.cmdb.microservice.IRltClassSvc;
import com.uino.service.util.FileUtil;
import com.uino.util.cache.ICacheService;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description: 元模型
 * @author: Lc
 * @create: 2021-11-04 10:49
 */
@Slf4j
@Service
public class VisualModelsPeer {

    // 编辑/取消编辑
    private static final Integer CANCEL_EDIT = 0;
    private static final Integer IS_EDIT = 1;

    private static final String VISUAL_MODEL_KEY = "VISUAL:MODEL:KEY:";

    @Autowired
    private ICacheService iCacheService;

    @Resource
    private IVisualModelApiSvc iVisualModelApiSvc;

    @Value("${local.resource.space}")
    private String localPath;

    @Autowired
    private IUserApiSvc userSvc;

    @Autowired
    private ICIClassSvc iciClassSvc;

    @Autowired
    private IRltClassSvc iRltClassSvc;

    @Autowired
    private ESVisualModelSvc visualModelSvc;

    /**
     * 处理元模型编辑或取消编辑
     *
     * @param modelId
     * @param status
     * @return
     */
    public Map<String, Object> handlerVisualModels(Long modelId, Integer status) {
        if (modelId == null) {
            throw new ServiceException("元模型唯一标识不能为空!");
        }
        SysUser userInfo = SysUtil.getCurrentUserInfo();
        if (userInfo == null) {
            throw new ServiceException("用户不存在!");
        }
        Long sysUserId = userInfo.getId();
        Map<String, Object> resultMap = new HashMap<>();

        List<ESVisualModel> esVisualModels = iVisualModelApiSvc.queryVisualModels(userInfo.getDomainId());
        List<String> visKey = new ArrayList<>();
        for (ESVisualModel esVisualModel : esVisualModels) {
            visKey.add(VISUAL_MODEL_KEY + esVisualModel.getId());
        }

        if (Objects.equals(status, IS_EDIT)) {
            // 查看当前元模型是否上锁
            Object obj = iCacheService.getCache(VISUAL_MODEL_KEY + modelId);
            if (obj != null) {
                Long cacheUserId = Long.valueOf(String.valueOf(obj));
                if (Objects.equals(sysUserId, cacheUserId)) {
                    resultMap.put("status", 1);
                    return resultMap;
                } else {
                    UserInfo user = userSvc.getUserInfoById(cacheUserId);
                    if (user != null && !StringUtils.isEmpty(user.getLoginCode())) {
                        resultMap.put("name", user.getLoginCode());
                    }
                    resultMap.put("status", 0);
                    return resultMap;
                }
            }
            // 因为元模型有多条数据 编辑时后台逻辑仅能控制当前编辑数据 但其他数据对于其他用户依然是不可编辑状态 将所有元模型上锁
            for (String vis : visKey) {
                iCacheService.setCache(vis, sysUserId, 60000*10);
            }
            // redisUtil.set(VISUAL_MODEL_KEY + modelId, sysUserId, 600);
            resultMap.put("status", 1);
            return resultMap;
        } else if (Objects.equals(status, CANCEL_EDIT)) {
            Object obj = iCacheService.getCache(VISUAL_MODEL_KEY + modelId);
            if (obj != null) {
                Long cacheUserId = Long.valueOf(String.valueOf(obj));
                if (Objects.equals(sysUserId, cacheUserId)) {
                    // 解锁全部模型
                    for (String vis : visKey) {
                        iCacheService.delKey(vis);
                    }
                    resultMap.put("status", 1);
                    return resultMap;
                } else {
                    resultMap.put("status", 0);
                    return resultMap;
                }
            }
            // 删除首次进来设置的值
            Object first = iCacheService.getCache(VISUAL_MODEL_KEY + 0);
            if (first != null) {
                iCacheService.delKey(VISUAL_MODEL_KEY + 0);
            }
            resultMap.put("status", 1);
            return resultMap;
        } else {
            throw new ServiceException("参数异常!");
        }
    }

    /**
     * 导出
     *
     * @return
     */
    public boolean exportVisualModels() {
        SysUser userInfo = SysUtil.getCurrentUserInfo();
        if (userInfo == null) {
            throw new ServiceException("用户不存在!");
        }
        List<ESVisualModel> esVisualModels = iVisualModelApiSvc.queryVisualModels(userInfo.getDomainId());
        for (ESVisualModel model : esVisualModels) {
            boolean enable = model.getEnable();
            if (Objects.equals(enable, false)) {
                continue;
            }
            String json = model.getJson();
            String filePath = Paths.get(FileFilterUtil.parseFilePath(localPath + "/" + LocalDate.now()), UUID.randomUUID().toString() + ".json").toString();
            try {
                FileUtil.writeFile(filePath, json.getBytes());
            } catch (IOException e) {
                throw new ServiceException("导出失败");
            }
        }
        return true;
    }

    /**
     * 导入
     * @param dto
     * @return
     */
    public boolean importVisualModels(VisualModelsDto dto) {
        checkVisualModel(dto);
        try {
            /*MultipartFile jsonFile = dto.getJsonFile();
            // 获取原始名字
            String fileName = jsonFile.getOriginalFilename();
            // 获取后缀名
            String suffixName = fileName.substring(fileName.lastIndexOf("."));
            //先将.json文件转为字符串类型
            File file = new File("/" + fileName);
            //将MultipartFile类型转换为File类型
            FileUtils.copyInputStreamToFile(jsonFile.getInputStream(), file);
            String jsonString = FileUtils.readFileToString(file, "UTF-8");
            //如果是json或者txt文件
            if (".json".equals(suffixName)) {*/
            //再将json字符串转为实体类
            SysUser userInfo = SysUtil.getCurrentUserInfo();
            List<ESVisualModel> models = iVisualModelApiSvc.queryVisualModels(userInfo.getDomainId());
            for (ESVisualModel esVisualModel : models) {
                boolean enable = esVisualModel.getEnable();
                if (Objects.equals(enable, false)) {
                    continue;
                }
                Long id = esVisualModel.getId();
                if (!Objects.equals(id, dto.getVisualModelId())) {
                    throw new ServiceException("元模型数据错误!");
                }
                JSONArray originalJsonArr = JSONArray.parseArray(esVisualModel.getJson());
                JSONArray newJsonArr = new JSONArray();
                for (int i = 0; i < originalJsonArr.size(); i++) {
                    JSONObject jsonObject = originalJsonArr.getJSONObject(i);
                    Long sheetId = jsonObject.getLong("sheetId");
                    if (sheetId == null) {
                        continue;
                    }
                    newJsonArr.add(jsonObject);
                    if (Objects.equals(dto.getSheetId(), sheetId)) {
                        JSONArray addJsonArr = JSONObject.parseArray(dto.getJsonFileStr());
                        for (int j = 0; j < addJsonArr.size(); j++) {
                            JSONObject addObject = addJsonArr.getJSONObject(j);
                            addObject.put("sheetId", ESUtil.getUUID());
                            addObject.put("active", false);
                            newJsonArr.add(addObject);
                        }
                    }
                }
                if(!Objects.equals(newJsonArr.toString(), esVisualModel.getJson())) {
                    esVisualModel.setJson(newJsonArr.toString());
                    iVisualModelApiSvc.saveVisualModel(esVisualModel);
                }
            }
            //}
        } catch (Exception e) {
            throw new ServiceException("导入文件出错");
        }
        return true;
    }

    /**
     * 通过sheetId获取元模型
     * @param sheetIds
     * @param domainId
     * @return
     */
    public List<VisualModelsVo> queryVisualModels(List<Long> sheetIds, Long domainId) {
        List<ESVisualModel> models = iVisualModelApiSvc.queryVisualModels(domainId);
        List<VisualModelsVo> visualModelsVos = new ArrayList<>();
        for (ESVisualModel visualModel : models) {
            boolean enable = visualModel.getEnable();
            if (Objects.equals(enable, false)) {
                continue;
            }
            VisualModelsVo visualModelsVo = new VisualModelsVo();
            BeanUtils.copyProperties(visualModel, visualModelsVo);
            String json = visualModelsVo.getJson();
            JSONArray jsonArray = JSONArray.parseArray(json);
            JSONArray newJsonArray = new JSONArray(jsonArray.size());
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                boolean hasSheetId = jsonObject.containsKey("sheetId");
                if (!hasSheetId) {
                    continue;
                }
                Long sheetId = jsonObject.getLong("sheetId");
                if (sheetIds.contains(sheetId)) {
                    newJsonArray.add(jsonObject);
                }
            }
            visualModelsVo.setJson(newJsonArray.toString());
            String thumbnail = visualModelsVo.getThumbnail();
            if (!StringUtils.isEmpty(thumbnail) && !thumbnail.startsWith("data:image/png;base64")) {
                Map<Long, String> map = (Map) JSONObject.parseObject(thumbnail);
                Iterator<Map.Entry<Long, String>> iterator = map.entrySet().iterator();
                List<ThumbnailVo> thumbnailList = new ArrayList<>();
                while (iterator.hasNext()) {
                    Map.Entry<Long, String> next = iterator.next();
                    String oldStatusIdId = String.valueOf(next.getKey());
                    boolean contains = sheetIds.contains(Long.valueOf(oldStatusIdId));
                    if (contains) {
                        for (int i = 0; i < newJsonArray.size(); i++) {
                            JSONObject jsonObject = newJsonArray.getJSONObject(i);
                            Long sheetId = jsonObject.getLong("sheetId");
                            if (Objects.equals(sheetId, Long.valueOf(oldStatusIdId))) {
                                ThumbnailVo thumbnailVo = new ThumbnailVo();
                                thumbnailVo.setName(jsonObject.getString("name"));
                                thumbnailVo.setThumbnail(next.getValue());
                                thumbnailList.add(thumbnailVo);
                            }
                        }
                    }
                }
                visualModelsVo.setThumbnailList(thumbnailList);
            }
            visualModelsVos.add(visualModelsVo);
        }
        return visualModelsVos;
    }

    public Long saveVisualModel(ESVisualModel model) {
        SysUser userInfo = SysUtil.getCurrentUserInfo();
        if (userInfo == null) {
            throw new ServiceException("用户不存在!");
        }
        model.setDomainId(userInfo.getDomainId());
        List<ESVisualModel> esVisualModels = iVisualModelApiSvc.queryVisualModels(userInfo.getDomainId());
        if (model.getId() != null) {
            Object obj = iCacheService.getCache(VISUAL_MODEL_KEY + model.getId());
            if (obj == null) {
                return 0L;
            }
            Long sysUserId = userInfo.getId();
            Long originalUserId = Long.valueOf(String.valueOf(obj));
            if (Objects.equals(sysUserId, originalUserId)) {
                for (ESVisualModel esVisualModel : esVisualModels) {
                    // 所有模型都上锁
                    iCacheService.setCache(VISUAL_MODEL_KEY + esVisualModel.getId(), sysUserId, 60000*10);     // 十分钟
                }
            } else {
                throw new BinaryException("无法获取用户信息，请重新登录");
            }
        }
        Long status = iVisualModelApiSvc.saveVisualModel(model);

        // 当前模型数据为新建 同原数据上锁
        if (BinaryUtils.isEmpty(model.getId())) {
            for (ESVisualModel esVisualModel : esVisualModels) {
                Object obj = iCacheService.getCache(VISUAL_MODEL_KEY + esVisualModel.getId());
                if (BinaryUtils.isEmpty(obj)) {
                    continue;
                }
                Long originalUserId = Long.valueOf(String.valueOf(obj));
                iCacheService.setCache(VISUAL_MODEL_KEY + status, originalUserId, 60000*10);
                return status;
            }
        }
        return status;
    }

    private void checkVisualModel(VisualModelsDto dto) {
        if (dto == null) {
            throw new ServiceException("导入元模型参数不能为空!");
        }
        if (StringUtils.isEmpty(dto.getJsonFileStr())) {
            throw new ServiceException("上传的json数据不能为空!");
        }
        if (dto.getVisualModelId() == null) {
            throw new ServiceException("元模型不能为空!");
        }
        if (dto.getSheetId() == null) {
            throw new ServiceException("sheetId不能为空!");
        }
    }


    public void delVMThumbnailBySheetId(Long id, Long sheetId) {
        if (BinaryUtils.isEmpty(sheetId)) {
            throw new ServiceException("sheetId不能为空!");
        }
        if (BinaryUtils.isEmpty(id)) {
            throw new ServiceException("元模型Id不能为空!");
        }
        iVisualModelApiSvc.delVMThumbnailBySheetId(id, sheetId);
    }


    public Map<Long, Long> refreshClassIdByVisId(Long visId) {
        Map<Long, Long> data = new HashMap<>();
        SysUser currentUserInfo = SysUtil.getCurrentUserInfo();
        // 根据visId查询元模型数据
        ESVisualModel visualModel = iVisualModelApiSvc.queryVisualModelById(currentUserInfo.getDomainId(), visId);
        if (BinaryUtils.isEmpty(visualModel)) {
            throw new BinaryException("元模型不存在");
        }
        // 查询所有class信息 code 和 id 组成 map
        CCcCiClass cdt = new CCcCiClass();
        cdt.setDomainId(currentUserInfo.getDomainId());
        List<CcCiClassInfo> ciClassList = iciClassSvc.queryClassByCdt(cdt);
        Map<String, Long> ciClassMap = ciClassList.stream().map(CcCiClassInfo::getCiClass).collect(Collectors.toMap(CcCiClass::getClassCode, CcCiClass::getId, (k1, k2) -> k2));

        List<CcCiClassInfo> rltClassList = iRltClassSvc.queryAllClasses(currentUserInfo.getDomainId());
        Map<String, Long> rltCodeMap = rltClassList.stream().map(CcCiClassInfo::getCiClass).collect(Collectors.toMap(CcCiClass::getClassCode, CcCiClass::getId, (k1, k2) -> k2));

        List<JSONObject> jsonList = JSON.parseArray(visualModel.getJson(), JSONObject.class);
        if (!BinaryUtils.isEmpty(jsonList)) {
            for (JSONObject json : jsonList) {
                String nodeDataArray = json.getString("nodeDataArray");
                if(BinaryUtils.isEmpty(nodeDataArray)){
                    continue;
                }
                List<JSONObject> nodeList = JSON.parseArray(nodeDataArray, JSONObject.class);
                Map<Integer, Long> keyClassMap = replaceCiClassId(nodeList, ciClassMap, data);
                json.put("nodeDataArray", JSON.toJSONString(nodeList));
                String linkDataArray = json.getString("linkDataArray");
                if(BinaryUtils.isEmpty(linkDataArray)){
                    continue;
                }
                List<JSONObject> linkList = JSON.parseArray(linkDataArray, JSONObject.class);
                replaceRltClassId(linkList, rltCodeMap, keyClassMap, data);
                json.put("linkDataArray", JSON.toJSONString(linkList));
            }
        }
        visualModel.setJson(JSON.toJSONString(jsonList));
        visualModelSvc.saveOrUpdate(visualModel);
        return data;
    }

    /**
     * 刷新元模型node节点分类id
     * @param nodeList 元模型节点集合
     * @param ciClassMap classCode->classId映射
     * @param data 刷新的classId映射
     * @return node节点key与匹配到的分类id映射
     */
    private Map<Integer, Long> replaceCiClassId(List<JSONObject> nodeList, Map<String, Long> ciClassMap, Map<Long, Long> data){
        Map<Integer, Long> keyClassMap = new HashMap<>();
        for (JSONObject node : nodeList) {
            String classCode = node.getString("classCode");
            Long classId = node.getLong("classId");
            if (!BinaryUtils.isEmpty(classCode)) {
                // 刷新数据中的classId
                Long id = ciClassMap.get(classCode);
                if (!BinaryUtils.isEmpty(id)) {
                    data.put(classId, id);
                    node.put("classId", id);
                    keyClassMap.put(node.getInteger("key"), id);
                } else {
                    // json中的classId 在全局分类匹配不到 当前导入分类按照图标处理
                    node.remove("classId");
                }
            } else {
                log.info("vis_nodeData========" + node.toJSONString());
            }
        }
        return keyClassMap;
    }

    /**
     * 刷新元模型关系线分类id
     * @param linkList 关系线集合
     * @param rltCodeMap 关系分类code->分类id映射
     * @param keyClassMap node节点key与匹配到的分类id映射
     * @param data 刷新的classId映射
     */
    private void replaceRltClassId(List<JSONObject> linkList, Map<String, Long> rltCodeMap, Map<Integer, Long> keyClassMap, Map<Long, Long> data){
        for (JSONObject link : linkList) {
            String classCode = link.getString("classCode");
            Integer from = link.getInteger("from");
            Integer to = link.getInteger("to");
            if(BinaryUtils.isEmpty(classCode)){
                log.info("vis_nodeData========" + link.toJSONString());
                continue;
            }
            Long id = rltCodeMap.get(classCode);
            if (keyClassMap.get(from) == null || keyClassMap.get(to) == null || BinaryUtils.isEmpty(id)) {
                // json中的classId 在全局分类匹配不到 当前导入分类按照图标处理
                link.remove("classId");
            }else{
                // 刷新数据中的classId
                data.put(link.getLong("classId"), id);
                link.put("classId", id);
            }
        }
    }

    public Boolean isEmploy(Long visId) {
        Long id = SysUtil.getCurrentUserInfo().getId();
        Object userId = iCacheService.getCache(VISUAL_MODEL_KEY + visId);
        // 只要当前用户没在登录状态都是false
        if (BinaryUtils.isEmpty(userId)) {
            // redis中无信息 当前用户可以编辑
            return false;
        }
        if (Objects.equals(id, Long.valueOf(userId.toString()))) {
            // 当前登录用户是编辑用户
            return true;
        }
        return false;
    }
}
